package sk.tuke.constraintbuilder.analyzer;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.StringTokenizer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class Analyzer {
	
	public static final String MARKER_ID =  "sk.tuke.constraintbuilder.annotationmarker";
	
	private File schema;
	
	public Analyzer(File schema){
		this.schema = schema;
	}
	
	public void examineResource(IResource resource) throws JavaModelException {
		if(JavaCore.create(resource) instanceof ICompilationUnit){
			JavaModelBuilder javaModelBuilder = new JavaModelBuilder();
			IJavaElement javaElement = JavaCore.create(resource);
			IType[] type = ((ICompilationUnit) javaElement).getAllTypes();
			for (int k = 0; k < type.length; k++) {
				if(!type[k].isMember()){
					File javaModel = javaModelBuilder.createResourceModel(type[k]);
					analyzeModel(type[k].getResource(), javaModel, schema);
				}
			}
		}	
	}
	
	private void analyzeModel(IResource resource, File javaModel, File schema){
		if(schema==null){
			System.out.println("Schema for resource "+resource.getName()+" not exists");
			return;
		}
		if(javaModel==null){
			System.out.println("Model for resource "+resource.getName()+" not exists");
			return;
		}
		File schematronResult = null;
		try {
			schematronResult = new ISOSchematronValidator().validate(schema, javaModel);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(schematronResult != null){
			analyzeOutput(schematronResult, resource);
		}
	}
	
	public void analyzeOutput(File validationFile, IResource resource){
		deleteMarkers(resource);
		Document document = null;
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = null;
        try {
            db = dbf.newDocumentBuilder();
            document = db.parse(validationFile);
            document.getDocumentElement().normalize();
        } catch (ParserConfigurationException | SAXException | IOException e) {  
        }   
        if(document != null){
            NodeList asserts = document.getDocumentElement().getElementsByTagName("svrl:failed-assert");
            for (int i = 0; i < asserts.getLength(); i++){
                AnnotationError error = parseMessage(asserts.item(i).getTextContent());
                addMarker((IFile) resource, error);
            }
        }
	}
	
    private AnnotationError parseMessage(String textContent) {
        StringTokenizer st = new StringTokenizer(textContent, "#");
        String  errorText = null;
		int i = 0, endChar = 0, startChar = 0, line = 0, severity = IMarker.SEVERITY_ERROR;
        while (st.hasMoreElements()) {
            if (i == 0) {
                errorText = st.nextToken().trim();
            } else if (i == 1) {
                line = Integer.parseInt(st.nextToken().trim());
            } else if (i == 2) {
                startChar =  Integer.parseInt(st.nextToken().trim());
            } else if (i == 3){
                endChar = Integer.parseInt(st.nextToken().trim());
            } else if (i == 4){
            	if(st.nextToken().trim().equals("WARNING"))severity = IMarker.SEVERITY_WARNING;
            }
            i++;
        }
        return new AnnotationError(errorText, line, startChar, endChar, severity);
    }
	
	private void addMarker(IFile file, AnnotationError error) {
        try {
        	int line = error.getLine();
        	if (error.getLine() == -1)line = 1;
        	IMarker marker = file.createMarker(MARKER_ID);
            marker.setAttribute(IMarker.MESSAGE, error.getMessage());
            marker.setAttribute(IMarker.SEVERITY, error.getSeverity());
            marker.setAttribute(IMarker.CHAR_START, error.getStartChar());
            marker.setAttribute(IMarker.CHAR_END, error.getEndChar());
            marker.setAttribute(IMarker.LINE_NUMBER, line);
        } catch (CoreException e) {
        }
	}
	
	public boolean deleteMarkers(IResource resource) {
		try {
			resource.deleteMarkers(MARKER_ID, false, IResource.DEPTH_INFINITE);
		    return true;
		}
		catch (CoreException e) {
			return false;
		}
	}
}
